package org.yang.parser;

import com.sun.istack.internal.Nullable;
import org.yang.parser.ast.Name;
import org.yang.parser.ast.Node;
import org.yang.parser.types.FunType;
import org.yang.parser.visitor.TypeInferencer;

import java.io.File;
import java.util.*;

public class Analyzer {

    // global static instance of analyzer itself
    public static Analyzer self;
    public TypeInferencer inferencer = new TypeInferencer();
    public String sid = $.newSessionId();
    public State moduleTable = new State(null, State.StateType.GLOBAL);
    public List<String> loadedFiles = new ArrayList<>();
    public State globalTable = new State(null, State.StateType.GLOBAL);
    public List<Binding> allBindings = new ArrayList<>();
    private Map<Node, List<Binding>> references = new LinkedHashMap<>();
    public Set<Name> resolved = new HashSet<>();
    public Set<Name> unresolved = new HashSet<>();
    public Map<String, List<Diagnostic>> semanticErors = new HashMap<>();
    public String cmd = null;
    public int nCalled = 0;
    public boolean multilineFunType = false;
    public List<String> path = new ArrayList<>();
    private Set<FunType> uncalled = new HashSet<>();
    private Set<Object> importStack = new HashSet<>();

    private AstCache astCache;
    public String cacheDir;
    public Set<String> failedToParse = new HashSet<>();
    public States states = new States();
    public Builtins builtins;
    private Progress loadingProgress = null;

    public String projectDir;
    public String modelDir;
    public Stack<CallStackEntry> callStack = new Stack<>();

    public Map<String, Object> options;

    public Analyzer(){
        this(null);
    }

    public Analyzer(Map<String, Object> options){
        self = this;
        if(options != null){
            this.options = options;
        } else {
            this.options = new HashMap<>();
        }
        this.states.putInt("startTime", System.currentTimeMillis());
        this.builtins = new Builtins();
        this.builtins.init();
        this.cacheDir = createCacheDir();
        this.astCache = new AstCache();
        addPythonPath();
        copyModels();
    }

    // main entry to the analyzer
    public void analyze(String path){
        String upath = $.unifyPath(path);
        File f = new File(upath);
        projectDir = f.isDirectory() ? f.getPath() : f.getParent();
        loadFileRecursive(upath);
    }

    /**
     * Load all html source files recursively if the given fullname is a directory;
     * otherwise just load file.
     * Looks at file extension to determine whether to load a give file.
     */
    public void loadFileRecursive(String fullname){
        int count = countFileRecursive(fullname);

        File file_or_dir = new File(fullname);
        if(file_or_dir.isDirectory()){
            for(File file : file_or_dir.listFiles()){
                loadFileRecursive(file.getPath());
            }
        } else {
            if(file_or_dir.getPath().endsWith(Globals.FILE_SUFFIX)){
                loadFile(file_or_dir.getPath());
            }
        }
    }

    // count number of .xhtml files
    public int countFileRecursive(String fullname){
        File file_or_dir = new File(fullname);
        int sum = 0;

        if(file_or_dir.isDirectory()){
            for(File file : file_or_dir.listFiles()){
                sum += countFileRecursive(file.getPath());
            }
        } else {
            if(file_or_dir.getPath().endsWith(Globals.FILE_SUFFIX)){
                sum += 1;
            }
        }
        return sum;
    }

    @Nullable
    public String loadFile(String path){
        path = $.unifyPath(path);
        File f = new File(path);

        if(!f.canRead()){
            return null;
        }


    }
}
